package com.itaming.lycheeframework.support.utils;

import com.itaming.lycheeframework.support.lambda.predicate.CharPredicate;
import lombok.experimental.UtilityClass;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.web.util.HtmlUtils;

import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

/**
 * String工具类
 *
 * @author A.Ming
 */
@UtilityClass
public class StringUtil {

    /**
     * 小写字母
     */
    private static final String LOWERCASE_CHARACTERS = "abcdefghijklmnopqrstuvwxyz";

    /**
     * 大写字母
     */
    private static final String UPPERCASE_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    /**
     * 数字
     */
    private static final String NUMERIC_CHARACTERS = "0123456789";

    /**
     * 特殊字符
     */
    private static final String SPECIAL_CHARACTERS = "!@#$%^&*()_+-=[]{}|;:,.<>?";

    /**
     * 字符填充长度限制
     */
    private static final int PAD_LIMIT = 8192;

    /**
     * 创建新字符串
     *
     * @param bytes byte数组
     * @return 创建的新字符串，当 {@code bytes} 为 {@code null} 时返回 {@code null}
     */
    public static String newString(byte[] bytes) {
        return newString(bytes, CharsetUtil.getDefault());
    }

    /**
     * 创建新字符串
     *
     * @param bytes   byte数组
     * @param charset 字符集
     * @return 创建的新字符串，当 {@code bytes} 为 {@code null} 时返回 {@code null}
     */
    public static String newString(byte[] bytes, Charset charset) {
        Assert.notNull(charset, "Charset must not be null");
        return bytes == null ? null : new String(bytes, charset);
    }

    /**
     * 创建新字符串
     *
     * @param chars char数组
     * @return 创建的新字符串，当 {@code chars} 为 {@code null} 时返回 {@code null}
     */
    public static String newString(char[] chars) {
        return chars == null ? null : new String(chars);
    }

    /**
     * 获取字符串，如果为 {@code null} 则返回默认值
     *
     * @param str        字符串
     * @param defaultStr 默认值
     * @return 如果字符串 {@code str} 为 {@code null} 则返回默认值，否则返回原字符串
     */
    public static String defaultIfNull(CharSequence str, String defaultStr) {
        return str == null ? defaultStr : str.toString();
    }

    /**
     * 获取字符串，如果为空则返回默认值
     *
     * @param str        字符串
     * @param defaultStr 默认值
     * @return 如果字符串 {@code str} 为空则返回默认值，否则返回原字符串
     */
    public static String defaultIfEmpty(CharSequence str, String defaultStr) {
        return isEmpty(str) ? defaultStr : str.toString();
    }

    /**
     * 获取字符串，如果为空白（blank）则返回默认值
     *
     * @param str        字符串
     * @param defaultStr 默认值
     * @return 如果字符串 {@code str} 为空白（blank）则返回默认值，否则返回原字符串
     */
    public static String defaultIfBlank(CharSequence str, String defaultStr) {
        return isBlank(str) ? defaultStr : str.toString();
    }

    /**
     * 获取字符串，如果为 {@code null} 则返回空字符串
     *
     * @param str 字符串
     * @return 如果 {@code str} 为 {@code null} 则返回空字符串，否则返回原字符串
     */
    @NonNull
    public static String emptyIfNull(CharSequence str) {
        return defaultIfNull(str, StringPool.EMPTY);
    }

    /**
     * 获取字符串长度
     *
     * @param str 字符串
     * @return 字符串长度
     */
    public static int length(CharSequence str) {
        return str == null ? 0 : str.length();
    }

    /**
     * 字符串转字节数组
     *
     * @param str 字符串
     * @return 字节数组，{@code str} 为 {@code null} 时返回 {@code null} ，{@code str} 为空时返回空数组
     */
    public static byte[] getBytes(CharSequence str) {
        return getBytes(str, CharsetUtil.getDefault());
    }

    /**
     * 字符串转字节数组，指定字符集
     *
     * @param str     字符串
     * @param charset 字符集
     * @return 字节数组，{@code str} 为 {@code null} 时返回 {@code null} ，{@code str} 为空时返回空数组
     */
    public static byte[] getBytes(CharSequence str, Charset charset) {
        Assert.notNull(charset, "Charset must not be null");
        return str == null ? null : str.toString().getBytes(charset);
    }

    /**
     * 字符串转字符数组
     *
     * @param str 字符串
     * @return 字符数组，{@code str} 为 {@code null} 时返回 {@code null} ，{@code str} 为空时返回空数组
     */
    public static char[] toCharArray(CharSequence str) {
        if (str == null) {
            return null;
        }

        if (str.isEmpty()) {
            return ArrayUtil.EMPTY_CHAR_ARRAY;
        }

        if (str instanceof String) {
            return ((String) str).toCharArray();
        }

        int len = str.length();
        char[] result = new char[len];
        for (int i = 0; i < len; i++) {
            result[i] = str.charAt(i);
        }

        return result;
    }

    /**
     * 生成指定长度的随机字符串
     *
     * @param length 字符串长度
     * @return 随机字符串
     */
    public static String random(int length) {
        return random(length, true, true);
    }

    /**
     * 生成指定长度的随机字符串
     *
     * @param length              字符串长度
     * @param includeNumbers      是否包含数字
     * @param includeSpecialChars 是否包含特殊字符
     * @return 随机字符串
     */
    public static String random(int length, boolean includeNumbers, boolean includeSpecialChars) {
        StringBuilder sb = new StringBuilder(length);
        String characters = LOWERCASE_CHARACTERS + UPPERCASE_CHARACTERS;
        if (includeNumbers) {
            characters += NUMERIC_CHARACTERS;
        }
        if (includeSpecialChars) {
            characters += SPECIAL_CHARACTERS;
        }

        ThreadLocalRandom random = ThreadLocalRandom.current();
        for (int i = 0; i < length; i++) {
            int randomIndex = random.nextInt(characters.length());
            char randomChar = characters.charAt(randomIndex);
            sb.append(randomChar);
        }

        return sb.toString();
    }

    /**
     * 生成 {@code UUID}
     *
     * @return {@code UUID} 字符串
     */
    @NonNull
    public static String UUID() {
        ThreadLocalRandom random = ThreadLocalRandom.current();
        return new UUID(random.nextLong(), random.nextLong()).toString();
    }

    /**
     * 生成简化的 {@code UUID}
     *
     * <p>
     * 简化的 {@code UUID} 是移除了中划线后的形式
     *
     * @return {@code UUID} 字符串
     */
    @NonNull
    public static String simpleUUID() {
        return UUID().replace(StringPool.DASH, StringPool.EMPTY);
    }

    /**
     * 判断字符串是否为空
     *
     * <p>
     * 空（empty）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     * </ul>
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean isEmpty(CharSequence str) {
        return str == null || str.isEmpty();
    }

    /**
     * 判断字符串是否不为空
     *
     * <p>
     * 空（empty）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     * </ul>
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean isNotEmpty(CharSequence str) {
        return !isEmpty(str);
    }

    /**
     * 判断任意一个字符串是否为空
     *
     * <p>
     * 空（empty）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     * </ul>
     *
     * @param strings 字符串数组
     * @return boolean
     */
    public static boolean isAnyEmpty(CharSequence... strings) {
        return isAnyEmpty(NullSafeUtil.asList(strings));
    }

    /**
     * 判断任意一个字符串是否为空
     *
     * <p>
     * 空（empty）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     * </ul>
     *
     * @param strings 字符串集合
     * @return boolean
     */
    public static boolean isAnyEmpty(Collection<? extends CharSequence> strings) {
        if (CollectionUtil.isEmpty(strings)) {
            return false;
        }

        for (CharSequence str : strings) {
            if (isEmpty(str)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 判断任意一个字符串是否不为空
     *
     * <p>
     * 空（empty）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     * </ul>
     *
     * @param strings 字符串数组
     * @return boolean
     */
    public static boolean isAnyNotEmpty(CharSequence... strings) {
        return isAnyNotEmpty(NullSafeUtil.asList(strings));
    }

    /**
     * 判断任意一个字符串是否不为空
     *
     * <p>
     * 空（empty）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     * </ul>
     *
     * @param strings 字符串集合
     * @return boolean
     */
    public static boolean isAnyNotEmpty(Collection<? extends CharSequence> strings) {
        if (CollectionUtil.isEmpty(strings)) {
            return false;
        }

        for (CharSequence str : strings) {
            if (isNotEmpty(str)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 判断所有字符串是否均为空
     *
     * <p>
     * 空（empty）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     * </ul>
     * <p>
     * 空数组将被视为 {@code true}，这是基于逻辑学中的 Vacuous Truth（空真）原则，
     * 当数组中没有元素时，所有元素满足某一条件的命题在逻辑上被认为为真。
     * 参考 <a href="https://en.wikipedia.org/wiki/Vacuous_truth">Vacuous Truth</a>
     * 中的 {@code In computer programming} 章节。
     * <p>
     * 此行为与主流工具库（如 Apache Commons 等）保持一致
     *
     * @param strings 字符串数组
     * @return boolean
     */
    public static boolean isAllEmpty(CharSequence... strings) {
        return isAllEmpty(NullSafeUtil.asList(strings));
    }

    /**
     * 判断所有字符串是否均为空
     *
     * <p>
     * 空（empty）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     * </ul>
     * <p>
     * 空集合将被视为 {@code true}，这是基于逻辑学中的 Vacuous Truth（空真）原则，
     * 当集合中没有元素时，所有元素满足某一条件的命题在逻辑上被认为为真。
     * 参考 <a href="https://en.wikipedia.org/wiki/Vacuous_truth">Vacuous Truth</a>
     * 中的 {@code In computer programming} 章节。
     * <p>
     * 此行为与主流工具库（如 Apache Commons 等）保持一致
     *
     * @param strings 字符串集合
     * @return boolean
     */
    public static boolean isAllEmpty(Collection<? extends CharSequence> strings) {
        return !isAnyNotEmpty(strings);
    }

    /**
     * 判断所有字符串是否均不为空
     *
     * <p>
     * 空（empty）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     * </ul>
     * <p>
     * 空数组将被视为 {@code true}，这是基于逻辑学中的 Vacuous Truth（空真）原则，
     * 当数组中没有元素时，所有元素满足某一条件的命题在逻辑上被认为为真。
     * 参考 <a href="https://en.wikipedia.org/wiki/Vacuous_truth">Vacuous Truth</a>
     * 中的 {@code In computer programming} 章节。
     * <p>
     * 此行为与主流工具库（如 Apache Commons 等）保持一致
     *
     * @param strings 字符串数组
     * @return boolean
     */
    public static boolean isAllNotEmpty(CharSequence... strings) {
        return isAllNotEmpty(NullSafeUtil.asList(strings));
    }

    /**
     * 判断所有字符串是否均不为空
     *
     * <p>
     * 空（empty）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     * </ul>
     * <p>
     * 空集合将被视为 {@code true}，这是基于逻辑学中的 Vacuous Truth（空真）原则，
     * 当集合中没有元素时，所有元素满足某一条件的命题在逻辑上被认为为真。
     * 参考 <a href="https://en.wikipedia.org/wiki/Vacuous_truth">Vacuous Truth</a>
     * 中的 {@code In computer programming} 章节。
     * <p>
     * 此行为与主流工具库（如 Apache Commons 等）保持一致
     *
     * @param strings 字符串集合
     * @return boolean
     */
    public static boolean isAllNotEmpty(Collection<? extends CharSequence> strings) {
        return !isAnyEmpty(strings);
    }

    /**
     * 判断字符串是否为空白
     *
     * <p>
     * 空白（blank）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     *     <li>字符串只包含空白字符（如空格、制表符、换行符等 Unicode 空白字符）</li>
     * </ul>
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean isBlank(CharSequence str) {
        if (isEmpty(str)) {
            return true;
        }

        for (int i = 0; i < str.length(); i++) {
            if (!CharacterUtil.isWhitespace(str.charAt(i))) {
                return false;
            }
        }

        return true;
    }

    /**
     * 判断字符串是否不为空白
     *
     * <p>
     * 空白（blank）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     *     <li>字符串只包含空白字符（如空格、制表符、换行符等 Unicode 空白字符）</li>
     * </ul>
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean isNotBlank(CharSequence str) {
        return !isBlank(str);
    }

    /**
     * 判断任意一个字符串是否为空白
     *
     * <p>
     * 空白（blank）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     *     <li>字符串只包含空白字符（如空格、制表符、换行符等 Unicode 空白字符）</li>
     * </ul>
     *
     * @param strings 字符串数组
     * @return boolean
     */
    public static boolean isAnyBlank(CharSequence... strings) {
        return isAnyBlank(NullSafeUtil.asList(strings));
    }

    /**
     * 判断任意一个字符串是否为空白
     *
     * <p>
     * 空白（blank）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     *     <li>字符串只包含空白字符（如空格、制表符、换行符等 Unicode 空白字符）</li>
     * </ul>
     *
     * @param strings 字符串集合
     * @return boolean
     */
    public static boolean isAnyBlank(Collection<? extends CharSequence> strings) {
        if (CollectionUtil.isEmpty(strings)) {
            return false;
        }

        for (CharSequence str : strings) {
            if (isBlank(str)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 判断任意一个字符串是否不为空白
     *
     * <p>
     * 空白（blank）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     *     <li>字符串只包含空白字符（如空格、制表符、换行符等 Unicode 空白字符）</li>
     * </ul>
     *
     * @param strings 字符串数组
     * @return boolean
     */
    public static boolean isAnyNotBlank(CharSequence... strings) {
        return isAnyNotBlank(NullSafeUtil.asList(strings));
    }

    /**
     * 判断任意一个字符串是否不为空白
     *
     * <p>
     * 空白（blank）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     *     <li>字符串只包含空白字符（如空格、制表符、换行符等 Unicode 空白字符）</li>
     * </ul>
     *
     * @param strings 字符串集合
     * @return boolean
     */
    public static boolean isAnyNotBlank(Collection<? extends CharSequence> strings) {
        if (CollectionUtil.isEmpty(strings)) {
            return false;
        }

        for (CharSequence str : strings) {
            if (isNotBlank(str)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 判断所有字符串是否均为空白
     *
     * <p>
     * 空白（blank）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     *     <li>字符串只包含空白字符（如空格、制表符、换行符等 Unicode 空白字符）</li>
     * </ul>
     * <p>
     * 空数组将被视为 {@code true}，这是基于逻辑学中的 Vacuous Truth（空真）原则，
     * 当数组中没有元素时，所有元素满足某一条件的命题在逻辑上被认为为真。
     * 参考 <a href="https://en.wikipedia.org/wiki/Vacuous_truth">Vacuous Truth</a>
     * 中的 {@code In computer programming} 章节。
     * <p>
     * 此行为与主流工具库（如 Apache Commons 等）保持一致
     *
     * @param strings 字符串数组
     * @return boolean
     */
    public static boolean isAllBlank(CharSequence... strings) {
        return isAllBlank(NullSafeUtil.asList(strings));
    }

    /**
     * 判断所有字符串是否均为空白
     *
     * <p>
     * 空白（blank）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     *     <li>字符串只包含空白字符（如空格、制表符、换行符等 Unicode 空白字符）</li>
     * </ul>
     *
     * <p>
     * 空集合将被视为 {@code true}，这是基于逻辑学中的 Vacuous Truth（空真）原则，
     * 当集合中没有元素时，所有元素满足某一条件的命题在逻辑上被认为为真。
     * 参考 <a href="https://en.wikipedia.org/wiki/Vacuous_truth">Vacuous Truth</a>
     * 中的 {@code In computer programming} 章节。
     *
     * <p>
     * 此行为与主流工具库（如 Apache Commons 等）保持一致
     *
     * @param strings 字符串集合
     * @return boolean
     */
    public static boolean isAllBlank(Collection<? extends CharSequence> strings) {
        return !isAnyNotBlank(strings);
    }

    /**
     * 判断所有字符串是否均不为空白
     *
     * <p>
     * 空白（blank）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     *     <li>字符串只包含空白字符（如空格、制表符、换行符等 Unicode 空白字符）</li>
     * </ul>
     *
     * <p>
     * 空数组将被视为 {@code true}，这是基于逻辑学中的 Vacuous Truth（空真）原则，
     * 当数组中没有元素时，所有元素满足某一条件的命题在逻辑上被认为为真。
     * 参考 <a href="https://en.wikipedia.org/wiki/Vacuous_truth">Vacuous Truth</a>
     * 中的 {@code In computer programming} 章节。
     *
     * <p>
     * 此行为与主流工具库（如 Apache Commons 等）保持一致
     *
     * @param strings 字符串数组
     * @return boolean
     */
    public static boolean isAllNotBlank(CharSequence... strings) {
        return isAllNotBlank(NullSafeUtil.asList(strings));
    }

    /**
     * 判断所有字符串是否均不为空白
     *
     * <p>
     * 空白（blank）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     *     <li>字符串只包含空白字符（如空格、制表符、换行符等 Unicode 空白字符）</li>
     * </ul>
     *
     * <p>
     * 空集合将被视为 {@code true}，这是基于逻辑学中的 Vacuous Truth（空真）原则，
     * 当集合中没有元素时，所有元素满足某一条件的命题在逻辑上被认为为真。
     * 参考 <a href="https://en.wikipedia.org/wiki/Vacuous_truth">Vacuous Truth</a>
     * 中的 {@code In computer programming} 章节。
     *
     * <p>
     * 此行为与主流工具库（如 Apache Commons 等）保持一致
     *
     * @param strings 字符串集合
     * @return boolean
     */
    public static boolean isAllNotBlank(Collection<? extends CharSequence> strings) {
        return !isAnyBlank(strings);
    }

    /**
     * 获取第一个不为空的字符串
     *
     * <p>
     * 空（empty）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     * </ul>
     *
     * @param strings 字符串数组
     * @return 第一个不为空的元素
     */
    @SafeVarargs
    public static <T extends CharSequence> T firstNotEmpty(T... strings) {
        return firstNotEmpty(NullSafeUtil.asList(strings));
    }

    /**
     * 获取第一个不为空的字符串
     *
     * <p>
     * 空（empty）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     * </ul>
     *
     * @param strings 字符串集合
     * @return 第一个不为空的元素
     */
    public static <T extends CharSequence> T firstNotEmpty(Collection<T> strings) {
        if (CollectionUtil.isEmpty(strings)) {
            return null;
        }

        for (T str : strings) {
            if (isNotEmpty(str)) {
                return str;
            }
        }

        return null;
    }

    /**
     * 获取第一个不为空白的字符串
     *
     * <p>
     * 空白（blank）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     *     <li>字符串只包含空白字符（如空格、制表符、换行符等 Unicode 空白字符）</li>
     * </ul>
     *
     * @param strings 字符串数组
     * @return 第一个不为空的元素
     */
    @SafeVarargs
    public static <T extends CharSequence> T firstNotBlank(T... strings) {
        return firstNotBlank(NullSafeUtil.asList(strings));
    }

    /**
     * 获取第一个不为空白的字符串
     *
     * <p>
     * 空白（blank）的定义包括以下几种情况：
     * <ul>
     *     <li>字符串为 null</li>
     *     <li>字符串长度为 0（即 ""）</li>
     *     <li>字符串只包含空白字符（如空格、制表符、换行符等 Unicode 空白字符）</li>
     * </ul>
     *
     * @param strings 字符串集合
     * @return 第一个不为空的元素
     */
    public static <T extends CharSequence> T firstNotBlank(Collection<T> strings) {
        if (CollectionUtil.isEmpty(strings)) {
            return null;
        }

        for (T str : strings) {
            if (isNotBlank(str)) {
                return str;
            }
        }

        return null;
    }

    /**
     * 判断字符串是否由小写字母组成
     *
     * <p>
     * 如果字符串为空或不包含字母，返回 {@code false}
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean isLowerCase(CharSequence str) {
        return isLowerCase(str, true);
    }

    /**
     * 判断字符串是否由小写字母组成
     *
     * <p>
     * 如果字符串为空或不包含字母，返回 {@code false}
     * <p>
     * 当 {@code ignoreNonLetters} 为 {@code true} 时，忽略非字母字符；
     * 否则，若存在非小写或非字母字符，则返回 {@code false}
     *
     * @param str              字符串
     * @param ignoreNonLetters 忽略非字母字符
     * @return boolean
     */
    public static boolean isLowerCase(CharSequence str, boolean ignoreNonLetters) {
        if (isEmpty(str)) {
            return false;
        }

        boolean hasLetter = false;

        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (Character.isLetter(c)) {
                hasLetter = true;
                if (!Character.isLowerCase(c)) {
                    return false;
                }
            } else if (!ignoreNonLetters) {
                // 不忽略非字母字符时，不是字母则必为 false
                return false;
            }
        }

        // 如果忽略非字母，则必须至少包含一个字母
        return !ignoreNonLetters || hasLetter;
    }

    /**
     * 判断字符串是否由大写字母组成，忽略非字母字符
     *
     * <p>
     * 如果字符串为空或不包含任何字母，则返回 {@code false}
     * <p>
     * 如果不希望忽略非字母字符，请使用 {@link #isUpperCase(CharSequence, boolean)}
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean isUpperCase(CharSequence str) {
        return isUpperCase(str, true);
    }

    /**
     * 判断字符串是否由大写字母组成
     *
     * <p>
     * 如果字符串为空或不包含任何字母，则返回 {@code false}
     * <p>
     * 当 {@code ignoreNonLetters} 为 {@code true} 时，忽略非字母字符；
     * 否则，若存在非大写或非字母字符，则返回 {@code false}
     *
     * @param str              字符串
     * @param ignoreNonLetters 忽略非字母字符
     * @return boolean
     */
    public static boolean isUpperCase(CharSequence str, boolean ignoreNonLetters) {
        if (isEmpty(str)) {
            return false;
        }

        boolean hasLetter = false;

        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);

            if (Character.isLetter(c)) {
                hasLetter = true;
                if (!Character.isUpperCase(c)) {
                    return false;
                }
            } else if (!ignoreNonLetters) {
                // 不忽略非字母字符时，不是字母则必为 false
                return false;
            }
        }

        // 如果忽略非字母，则必须至少包含一个字母
        return !ignoreNonLetters || hasLetter;
    }

    /**
     * 判断字符串是否只包含数字
     *
     * <p>
     * 字符串为空时返回 {@code false}
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean isNumeric(CharSequence str) {
        return allCharMatches(str, CharacterUtil::isDigit);
    }

    /**
     * 判断字符串是否只包含数字或空格
     *
     * <p>
     * 字符串为空时返回 {@code false}
     * <p>
     * 注意：在 {@code Apache Commons} 工具库中，字符串为空（""）时返回结果是 {@code true}，
     * 使用此方法时须知晓上述逻辑差异！
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean isNumericSpace(CharSequence str) {
        return allCharMatches(str, CharacterUtil::isDigitSpace);
    }

    /**
     * 判断字符串是否只包含字母
     *
     * <p>
     * 字符串为空时返回 {@code false}
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean isAlpha(CharSequence str) {
        return allCharMatches(str, CharacterUtil::isLetter);
    }

    /**
     * 判断字符串是否只包含字母或空格
     *
     * <p>
     * 字符串为空时返回 {@code false}
     * <p>
     * 注意：在 {@code Apache Commons} 工具库中，字符串为空（""）时返回结果是 {@code true}，
     * 使用此方法时须知晓上述逻辑差异！
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean isAlphaSpace(CharSequence str) {
        return allCharMatches(str, CharacterUtil::isLetterSpace);
    }

    /**
     * 判断字符串是否只包含字母或数字
     *
     * <p>
     * 字符串为空时返回 {@code false}
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean isAlphanumeric(CharSequence str) {
        return allCharMatches(str, CharacterUtil::isAlphaNumber);
    }

    /**
     * 判断字符串是否只包含字母、数字或空格
     *
     * <p>
     * 字符串为空时返回 {@code false}
     * <p>
     * 注意：在 {@code Apache Commons} 工具库中，字符串为空（""）时返回结果是 {@code true}，
     * 使用此方法时须知晓上述逻辑差异！
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean isAlphanumericSpace(CharSequence str) {
        return allCharMatches(str, CharacterUtil::isAlphaNumberSpace);
    }

    /**
     * 判断两个字符串是否相同
     *
     * @param str1 字符串1
     * @param str2 字符串2
     * @return boolean
     */
    public static boolean equals(CharSequence str1, CharSequence str2) {
        if (str1 == str2) {
            return true;
        }

        if (str1 == null || str2 == null) {
            return false;
        }

        if (str1.length() != str2.length()) {
            return false;
        }

        if (str1 instanceof String && str2 instanceof String) {
            return str1.equals(str2);
        }

        for (int i = 0; i < str1.length(); ++i) {
            if (str1.charAt(i) != str2.charAt(i)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 判断两个字符串是否相同，忽略大小写
     *
     * @param str1 字符串1
     * @param str2 字符串2
     * @return boolean
     */
    public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) {
        if (str1 == str2) {
            return true;
        }

        if (str1 == null || str2 == null) {
            return false;
        }

        if (str1.length() != str2.length()) {
            return false;
        }

        return regionMatches(str1, true, 0, str2, 0, str1.length());
    }

    /**
     * 判断字符串是否与要比较的字符串中的任意一个相同
     *
     * @param str     字符串
     * @param strings 要比较的字符串
     * @return boolean
     */
    public static boolean equalsAny(CharSequence str, CharSequence... strings) {
        return equalsAny(str, NullSafeUtil.asList(strings));
    }

    /**
     * 判断字符串是否与要比较的字符串中的任意一个相同
     *
     * @param str     字符串
     * @param strings 要比较的字符串
     * @return boolean
     */
    public static boolean equalsAny(CharSequence str, Collection<? extends CharSequence> strings) {
        if (CollectionUtil.isEmpty(strings)) {
            return false;
        }

        for (CharSequence s : strings) {
            if (equals(str, s)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 判断字符串是否与要比较的字符串中的任意一个相同，忽略大小写
     *
     * @param str     字符串
     * @param strings 要比较的字符串
     * @return boolean
     */
    public static boolean equalsAnyIgnoreCase(CharSequence str, CharSequence... strings) {
        return equalsAnyIgnoreCase(str, NullSafeUtil.asList(strings));
    }

    /**
     * 判断字符串是否与要比较的字符串中的任意一个相同，忽略大小写
     *
     * @param str     字符串
     * @param strings 要比较的字符串
     * @return boolean
     */
    public static boolean equalsAnyIgnoreCase(CharSequence str, Collection<? extends CharSequence> strings) {
        if (CollectionUtil.isEmpty(strings)) {
            return false;
        }

        for (CharSequence s : strings) {
            if (equalsIgnoreCase(str, s)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 判断字符串是否包含要查找的字符串
     *
     * @param str       字符串
     * @param searchStr 要查找的字符串
     * @return boolean
     */
    public static boolean contains(CharSequence str, CharSequence searchStr) {
        return indexOf(str, searchStr, 0) >= 0;
    }

    /**
     * 判断字符串是否包含要查找的字符串，忽略大小写
     *
     * @param str       字符串
     * @param searchStr 要查找的字符串
     * @return boolean
     */
    public static boolean containsIgnoreCase(CharSequence str, CharSequence searchStr) {
        return indexOfIgnoreCase(str, searchStr) >= 0;
    }

    /**
     * 判断字符串是否包含任意一个要查找的字符串
     *
     * <p>
     * 字符串 {@code str} 为 {@code null} 或要查找的字符串数组为空时，返回 {@code false}
     *
     * @param str           字符串
     * @param searchStrings 要查找的字符串
     * @return boolean
     */
    public static boolean containsAny(CharSequence str, CharSequence... searchStrings) {
        return containsAny(str, NullSafeUtil.asList(searchStrings));
    }

    /**
     * 判断字符串是否包含任意一个要查找的字符串
     *
     * <p>
     * 字符串 {@code str} 为 {@code null} 或要查找的字符串集合为空时，返回 {@code false}
     *
     * @param str           字符串
     * @param searchStrings 要查找的字符串
     * @return boolean
     */
    public static boolean containsAny(CharSequence str, Collection<? extends CharSequence> searchStrings) {
        if (str == null || CollectionUtil.isEmpty(searchStrings)) {
            return false;
        }

        for (CharSequence s : searchStrings) {
            if (contains(str, s)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 判断字符串是否包含任意一个要查找的字符串，忽略大小写
     *
     * <p>
     * 字符串 {@code str} 为 {@code null} 或要查找的字符串数组为空时，返回 {@code false}
     *
     * @param str           字符串
     * @param searchStrings 要查找的字符串
     * @return boolean
     */
    public static boolean containsAnyIgnoreCase(CharSequence str, CharSequence... searchStrings) {
        return containsAnyIgnoreCase(str, NullSafeUtil.asList(searchStrings));
    }

    /**
     * 判断字符串是否包含任意一个要查找的字符串，忽略大小写
     *
     * <p>
     * 字符串 {@code str} 为 {@code null} 或要查找的字符串集合为空时，返回 {@code false}
     *
     * @param str           字符串
     * @param searchStrings 要查找的字符串
     * @return boolean
     */
    public static boolean containsAnyIgnoreCase(CharSequence str, Collection<? extends CharSequence> searchStrings) {
        if (str == null || CollectionUtil.isEmpty(searchStrings)) {
            return false;
        }

        for (CharSequence s : searchStrings) {
            if (containsIgnoreCase(str, s)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 判断字符串是否包含所有要查找的字符串
     *
     * <p>
     * 字符串 {@code str} 为 {@code null} 或要查找的字符串数组为空时，返回 {@code false}
     *
     * @param str           字符串
     * @param searchStrings 要查找的字符串
     * @return boolean
     */
    public static boolean containsAll(CharSequence str, CharSequence... searchStrings) {
        return containsAll(str, NullSafeUtil.asList(searchStrings));
    }

    /**
     * 判断字符串是否包含所有要查找的字符串
     *
     * <p>
     * 字符串 {@code str} 为 {@code null} 或要查找的字符串集合为空时，返回 {@code false}
     *
     * @param str           字符串
     * @param searchStrings 要查找的字符串
     * @return boolean
     */
    public static boolean containsAll(CharSequence str, Collection<? extends CharSequence> searchStrings) {
        if (str == null || CollectionUtil.isEmpty(searchStrings)) {
            return false;
        }

        for (CharSequence s : searchStrings) {
            if (!contains(str, s)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 判断字符串是否包含所有要查找的字符串，忽略大小写
     *
     * <p>
     * 字符串 {@code str} 为 {@code null} 或要查找的字符串数组为空时，返回 {@code false}
     *
     * @param str           字符串
     * @param searchStrings 要查找的字符串
     * @return boolean
     */
    public static boolean containsAllIgnoreCase(CharSequence str, CharSequence... searchStrings) {
        return containsAllIgnoreCase(str, NullSafeUtil.asList(searchStrings));
    }

    /**
     * 判断字符串是否包含所有要查找的字符串，忽略大小写
     *
     * <p>
     * 字符串 {@code str} 为 {@code null} 或要查找的字符串集合为空时，返回 {@code false}
     *
     * @param str           字符串
     * @param searchStrings 要查找的字符串
     * @return boolean
     */
    public static boolean containsAllIgnoreCase(CharSequence str, Collection<? extends CharSequence> searchStrings) {
        if (str == null || CollectionUtil.isEmpty(searchStrings)) {
            return false;
        }

        for (CharSequence s : searchStrings) {
            if (!containsIgnoreCase(str, s)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 判断字符串是否不包含所有要查找的字符串
     *
     * <p>
     * 字符串 {@code str} 为 {@code null} 或要查找的字符串集合为空时，返回 {@code true}
     *
     * @param str           字符串
     * @param searchStrings 要查找的字符串
     * @return boolean
     */
    public static boolean containsNone(CharSequence str, CharSequence... searchStrings) {
        return containsNone(str, NullSafeUtil.asList(searchStrings));
    }

    /**
     * 判断字符串是否不包含所有要查找的字符串
     *
     * <p>
     * 字符串 {@code str} 为 {@code null} 或要查找的字符串集合为空时，返回 {@code true}
     *
     * @param str           字符串
     * @param searchStrings 要查找的字符串
     * @return boolean
     */
    public static boolean containsNone(CharSequence str, Collection<? extends CharSequence> searchStrings) {
        return !containsAny(str, searchStrings);
    }

    /**
     * 判断字符串是否不包含所有要查找的字符串，忽略大小写
     *
     * <p>
     * 字符串 {@code str} 为 {@code null} 或要查找的字符串集合为空时，返回 {@code true}
     *
     * @param str           字符串
     * @param searchStrings 要查找的字符串
     * @return boolean
     */
    public static boolean containsNoneIgnoreCase(CharSequence str, CharSequence... searchStrings) {
        return containsNoneIgnoreCase(str, NullSafeUtil.asList(searchStrings));
    }

    /**
     * 判断字符串是否不包含所有要查找的字符串，忽略大小写
     *
     * <p>
     * 字符串 {@code str} 为 {@code null} 或要查找的字符串集合为空时，返回 {@code true}
     *
     * @param str           字符串
     * @param searchStrings 要查找的字符串
     * @return boolean
     */
    public static boolean containsNoneIgnoreCase(CharSequence str, Collection<? extends CharSequence> searchStrings) {
        return !containsAnyIgnoreCase(str, searchStrings);
    }

    /**
     * 判断字符串是否包含空白字符
     *
     * @param str 字符串
     * @return boolean
     */
    public static boolean containsWhitespace(CharSequence str) {
        if (isEmpty(str)) {
            return false;
        }

        for (int i = 0; i < str.length(); i++) {
            if (CharacterUtil.isWhitespace(str.charAt(i))) {
                return true;
            }
        }

        return false;
    }

    /**
     * 拼接字符串
     *
     * @param strings 要拼接的字符串
     * @return 拼接的字符串，如果 {@code strings} 为空，则返回空字符串
     */
    @NonNull
    public static String join(Collection<? extends CharSequence> strings) {
        return join(null, strings);
    }

    /**
     * 使用指定分隔符拼接字符串
     *
     * @param delimiter 分隔符
     * @param strings   要拼接的字符串
     * @return 拼接的字符串，如果 {@code strings} 为空，则返回空字符串
     */
    @NonNull
    public static String join(CharSequence delimiter, Iterable<? extends CharSequence> strings) {
        if (CollectionUtil.isEmpty(strings)) {
            return StringPool.EMPTY;
        }
        return String.join(emptyIfNull(delimiter), strings);
    }

    /**
     * 拼接字符串
     *
     * @param elements 要拼接的字符串
     * @return 拼接的字符串，如果 {@code elements} 为空，则返回空字符串
     */
    @NonNull
    public static String join(CharSequence... elements) {
        return join(null, elements);
    }

    /**
     * 使用指定分隔符拼接字符串
     *
     * @param delimiter 分隔符
     * @param elements  要拼接的字符串
     * @return 拼接的字符串，如果 {@code elements} 为空，则返回空字符串
     */
    @NonNull
    public static String join(CharSequence delimiter, CharSequence... elements) {
        if (ArrayUtil.isEmpty(elements)) {
            return StringPool.EMPTY;
        }
        return String.join(emptyIfNull(delimiter), elements);
    }

    /**
     * 分隔字符串
     *
     * <p>
     * 默认使用空白符分割，空白符的认定参考 {@link Character#isWhitespace(char)}。
     * 如果需要自定分隔符，请使用{@link #split(CharSequence, CharSequence)}
     *
     * @param str 字符串
     * @return 分割后的字符串数组
     */
    @NonNull
    public static String[] split(CharSequence str) {
        return split(str, null);
    }

    /**
     * 分隔字符串
     *
     * <p>
     * 默认保留所有分隔符，即相邻的分隔符之间将被认为存在一个空字符串的元素，
     * 如果需要将相邻的分隔符作为一个分隔符，
     * 请使用{@link #split(CharSequence, CharSequence, boolean)}
     *
     * <p>
     * 注意：此行为与 {@link String#split(String)} 的行为一致，
     * 但与 {@code Apache Commons} 行为相反，其默认行为是不保留
     *
     * @param str       字符串
     * @param separator 分隔符
     * @return 分割后的字符串数组
     */
    @NonNull
    public static String[] split(CharSequence str, CharSequence separator) {
        return split(str, separator, true);
    }

    /**
     * 分隔字符串
     *
     * <p>
     * 当 {@code preserveAllTokens} 为 {@code true} 时，两个分隔符之间的空字符串也会被保留
     * 为 {@code false} 时，两个相邻的分隔符会被认为是一个分隔符。
     *
     * @param str               字符串
     * @param separator         分隔符
     * @param preserveAllTokens 是否保留所有分段
     * @return 分割后的字符串数组
     */
    @NonNull
    public static String[] split(CharSequence str, CharSequence separator, boolean preserveAllTokens) {
        if (isEmpty(str)) {
            return ArrayUtil.EMPTY_STRING_ARRAY;
        }

        // 默认使用空白符分割
        if (isEmpty(separator)) {
            return splitByWhitespace(str, true);
        }

        int strLen = str.length();
        int separatorLength = separator.length();

        ArrayList<String> substrings = new ArrayList<>();
        int next = 0;
        int end = 0;
        while (end < strLen) {
            end = indexOf(str, separator, next);
            if (end > -1) {
                if (end > next) {
                    substrings.add(substring(str, next, end));
                } else {
                    // 如果保留所有分段，则相邻分隔符之间应存在一个空字符串
                    if (preserveAllTokens) {
                        substrings.add(StringPool.EMPTY);
                    }
                }
                next = end + separatorLength;
            } else {
                substrings.add(substring(str, next));
                end = strLen;
            }
        }

        return substrings.toArray(ArrayUtil.EMPTY_STRING_ARRAY);
    }

    /**
     * 获取字符串中要查找的字符串第一次出现位置的索引
     *
     * @param str       字符串
     * @param searchStr 要查找的字符串
     * @return 索引，未找到返回 {@code -1}
     */
    public static int indexOf(CharSequence str, CharSequence searchStr) {
        return indexOf(str, searchStr, 0);
    }

    /**
     * 获取字符串中要查找的字符串第一次出现位置的索引
     *
     * @param str       字符串
     * @param searchStr 要查找的字符串
     * @param fromIndex 起始位置索引
     * @return 索引，未找到返回 {@code -1}
     */
    public static int indexOf(CharSequence str, CharSequence searchStr, int fromIndex) {
        if (str == null || searchStr == null) {
            return -1;
        }

        if (str instanceof String) {
            return ((String) str).indexOf(searchStr.toString(), fromIndex);
        }

        if (str instanceof StringBuffer) {
            return ((StringBuffer) str).indexOf(searchStr.toString(), fromIndex);
        }

        if (str instanceof StringBuilder) {
            return ((StringBuilder) str).indexOf(searchStr.toString(), fromIndex);
        }

        return str.toString().indexOf(searchStr.toString(), fromIndex);
    }

    /**
     * 获取字符串中要查找的字符串第一次出现位置的索引，忽略大小写
     *
     * @param str    字符串
     * @param search 查找字符串
     * @return 索引，未找到返回 {@code -1}
     */
    public static int indexOfIgnoreCase(CharSequence str, CharSequence search) {
        return indexOfIgnoreCase(str, search, 0);
    }

    /**
     * 获取字符串中要查找的字符串第一次出现位置的索引，忽略大小写
     *
     * @param str       字符串
     * @param searchStr 要查找的字符串
     * @param fromIndex 起始位置索引
     * @return 索引，未找到返回 {@code -1}
     */
    public static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr, int fromIndex) {
        if (str == null || searchStr == null) {
            return -1;
        }

        if (fromIndex < 0) {
            fromIndex = 0;
        }

        int strLength = str.length();
        int searchStrLength = searchStr.length();

        int endLimit = strLength - searchStrLength + 1;
        if (fromIndex > endLimit) {
            return -1;
        }

        // 要查找的字符串为空
        if (searchStrLength == 0) {
            return fromIndex;
        }

        for (int i = fromIndex; i < endLimit; i++) {
            if (regionMatches(str, true, i, searchStr, 0, searchStrLength)) {
                return i;
            }
        }

        return -1;
    }

    /**
     * 获取字符串中要查找的字符串最后一次出现位置的索引
     *
     * @param str       字符串
     * @param searchStr 要查找的字符串
     * @return 索引，未找到返回 {@code -1}
     */
    public static int lastIndexOf(CharSequence str, CharSequence searchStr) {
        return lastIndexOf(str, searchStr, Integer.MAX_VALUE);
    }

    /**
     * 获取字符串中要查找的字符串最后一次出现位置的索引
     *
     * <p>
     * 从 {@code fromIndex} 开始向字符串开头方向查找(反向查找)，
     * 如果 {@code fromIndex} 超出字符串长度，则从字符串末尾开始查找，
     * 如果 {@code fromIndex} 小于 {@code 0}，则返回 {@code -1}
     *
     * @param str       字符串
     * @param searchStr 要查找的字符串
     * @param fromIndex 起始位置索引
     * @return 索引，未找到返回 {@code -1}
     */
    public static int lastIndexOf(CharSequence str, CharSequence searchStr, int fromIndex) {
        if (searchStr == null || str == null) {
            return -1;
        }

        if (str instanceof String) {
            return ((String) str).lastIndexOf(searchStr.toString(), fromIndex);
        }

        if (str instanceof StringBuffer) {
            return ((StringBuffer) str).lastIndexOf(searchStr.toString(), fromIndex);
        }

        if (str instanceof StringBuilder) {
            return ((StringBuilder) str).lastIndexOf(searchStr.toString(), fromIndex);
        }

        return str.toString().lastIndexOf(searchStr.toString(), fromIndex);
    }

    /**
     * 获取字符串中要查找的字符串最后一次出现位置的索引，忽略大小写
     *
     * @param str       字符串
     * @param searchStr 要查找的字符串
     * @return 索引，未找到返回 {@code -1}
     */
    public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr) {
        return lastIndexOfIgnoreCase(str, searchStr, Integer.MAX_VALUE);
    }

    /**
     * 获取字符串中要查找的字符串最后一次出现位置的索引，忽略大小写
     *
     * <p>
     * 从 {@code fromIndex} 开始向字符串开头方向查找(反向查找)，
     * 如果 {@code fromIndex} 超出字符串长度，则从字符串末尾开始查找，
     * 如果 {@code fromIndex} 小于 {@code 0}，则返回 {@code -1}
     *
     * @param str       字符串
     * @param searchStr 要查找的字符串
     * @param fromIndex 起始位置索引
     * @return 索引，未找到返回 {@code -1}
     */
    public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr, int fromIndex) {
        if (str == null || searchStr == null) {
            return -1;
        }

        int strLength = str.length();
        int searchStrLength = searchStr.length();

        int rightIndex = strLength - searchStrLength;
        if (fromIndex > rightIndex) {
            fromIndex = rightIndex;
        }

        if (fromIndex < 0) {
            return -1;
        }

        // 要查找的字符串为空
        if (searchStrLength == 0) {
            return fromIndex;
        }

        for (int i = fromIndex; i >= 0; i--) {
            if (regionMatches(str, true, i, searchStr, 0, searchStrLength)) {
                return i;
            }
        }

        return -1;
    }

    /**
     * 获取指定位置索引到字符串末尾的子字符串
     *
     * <p>
     * 负数索引表示从字符串末尾开始计算索引，
     * 如：{@code -1} 表示最后一个字符，-2 表示倒数第二个字符，以此类推
     *
     * @param str        字符串
     * @param beginIndex 起始位置索引
     * @return 子字符串
     */
    public static String substring(CharSequence str, int beginIndex) {
        return substring(str, beginIndex, Integer.MAX_VALUE);
    }

    /**
     * 获取指定位置索引之间的子字符串
     *
     * <p>
     * 负数索引表示从字符串末尾开始计算索引，
     * 如：{@code -1} 表示最后一个字符，-2 表示倒数第二个字符，以此类推
     *
     * @param str        字符串
     * @param beginIndex 起始位置索引
     * @param endIndex   结束位置索引(不包含)
     * @return 子字符串
     */
    public static String substring(CharSequence str, int beginIndex, int endIndex) {
        if (isEmpty(str)) {
            return str == null ? null : str.toString();
        }

        // 处理负数索引
        if (beginIndex < 0) {
            beginIndex = str.length() + beginIndex;
        }
        if (endIndex < 0) {
            endIndex = str.length() + endIndex;
        }

        // 约束索引
        if (beginIndex < 0) {
            beginIndex = 0;
        }
        if (endIndex > str.length()) {
            endIndex = str.length();
        }

        // 起始位置索引大于结束位置索引，返回空字符串
        if (beginIndex >= endIndex) {
            return StringPool.EMPTY;
        }

        return str.subSequence(beginIndex, endIndex).toString();
    }

    /**
     * 获取指定分隔符首次出现位置之前的子字符串
     *
     * <p>
     * 在字符串中查找指定分隔符（Unicode 字符）首次出现的位置，并返回其前面部分
     *
     * @param str       字符串
     * @param delimiter 分隔符（Unicode 字符）
     * @return 子字符串，如果未找到分隔符 {@code delimiter} 则返回原字符串；如果 {@code str} 为 {@code null}，则返回 {@code null}
     */
    public static String substringBefore(CharSequence str, int delimiter) {
        return substringBefore(str, delimiter, false);
    }

    /**
     * 获取指定分隔符最后一次出现位置之前的子字符串
     *
     * <p>
     * 在字符串中查找指定分隔符（Unicode 字符）最后一次出现的位置，并返回其前面部分
     *
     * @param str       字符串
     * @param delimiter 分隔符（Unicode 字符）
     * @return 子字符串，如果未找到分隔符 {@code delimiter} 则返回原字符串；如果 {@code str} 为 {@code null}，则返回 {@code null}
     */
    public static String substringBeforeLast(CharSequence str, int delimiter) {
        return substringBefore(str, delimiter, true);
    }

    /**
     * 获取指定分隔符之前的子字符串
     *
     * <p>
     * 在字符串中查找指定分隔符（Unicode 字符）出现的位置，并返回其前面部分
     *
     * @param str         字符串
     * @param delimiter   分隔符（Unicode 字符）
     * @param isLastIndex 是否使用最后一次出现位置的索引
     * @return 子字符串，如果未找到分隔符 {@code delimiter} 则返回原字符串；如果 {@code str} 为 {@code null}，则返回 {@code null}
     */
    private static String substringBefore(CharSequence str, int delimiter, boolean isLastIndex) {
        if (isEmpty(str)) {
            return str == null ? null : str.toString();
        }

        String s = str.toString();
        int index = isLastIndex ? s.lastIndexOf(delimiter) : s.indexOf(delimiter);
        if (index == -1) {
            return str.toString();
        }
        if (index == 0) {
            return StringPool.EMPTY;
        }

        return substring(str, 0, index);
    }

    /**
     * 获取指定分隔符首次出现位置之前的子字符串
     *
     * <p>
     * 在字符串中查找指定分隔符（字符串）首次出现的位置，并返回其前面部分
     *
     * @param str       字符串
     * @param delimiter 分隔符（字符串）
     * @return 子字符串，如果未找到分隔符 {@code delimiter} 则返回原字符串；如果 {@code str} 为 {@code null}，则返回 {@code null}
     */
    public static String substringBefore(CharSequence str, CharSequence delimiter) {
        return substringBefore(str, delimiter, false);
    }

    /**
     * 获取指定分隔符首次出现位置之前的子字符串
     *
     * <p>
     * 在字符串中查找指定分隔符（字符串）最后一次出现的位置，并返回其前面部分
     *
     * @param str       字符串
     * @param delimiter 分隔符（字符串）
     * @return 子字符串，如果未找到分隔符 {@code delimiter} 则返回原字符串；如果 {@code str} 为 {@code null}，则返回 {@code null}
     */
    public static String substringBeforeLast(CharSequence str, CharSequence delimiter) {
        return substringBefore(str, delimiter, true);
    }

    /**
     * 获取指定分隔符之前的子字符串
     *
     * <p>
     * 在字符串中查找指定分隔符（字符串）出现的位置，并返回其前面部分
     *
     * @param str         字符串
     * @param delimiter   分隔符（字符串）
     * @param isLastIndex 是否使用最后一次出现位置的索引
     * @return 子字符串，如果未找到分隔符 {@code delimiter} 则返回原字符串；如果 {@code str} 为 {@code null}，则返回 {@code null}
     */
    private static String substringBefore(CharSequence str, CharSequence delimiter, boolean isLastIndex) {
        if (isEmpty(str) || delimiter == null) {
            return str == null ? null : str.toString();
        }

        if (delimiter.isEmpty()) {
            return isLastIndex ? str.toString() : StringPool.EMPTY;
        }

        int index = isLastIndex ? lastIndexOf(str, delimiter) : indexOf(str, delimiter);
        if (index == -1) {
            return str.toString();
        }

        if (index == 0) {
            return StringPool.EMPTY;
        }

        return substring(str, 0, index);
    }

    /**
     * 获取指定分隔符首次出现位置之后的子字符串
     *
     * <p>
     * 在字符串中查找指定分隔符（Unicode 字符）首次出现的位置，并返回其后面部分
     *
     * @param str       字符串
     * @param delimiter 分隔符（Unicode 字符）
     * @return 子字符串，如果未找到分隔符 {@code delimiter} 则返回原字符串；如果 {@code str} 为 {@code null}，则返回 {@code null}
     */
    public static String substringAfter(CharSequence str, int delimiter) {
        return substringAfter(str, delimiter, false);
    }

    /**
     * 获取指定分隔符最后一次出现位置之后的子字符串
     *
     * <p>
     * 在字符串中查找指定分隔符（Unicode 字符）最后一次出现的位置，并返回其后面部分
     *
     * @param str       字符串
     * @param delimiter 分隔符（Unicode 字符）
     * @return 子字符串，如果未找到分隔符 {@code delimiter} 则返回原字符串；如果 {@code str} 为 {@code null}，则返回 {@code null}
     */
    public static String substringAfterLast(CharSequence str, int delimiter) {
        return substringAfter(str, delimiter, true);
    }

    /**
     * 获取指定分隔符之后的子字符串
     *
     * <p>
     * 在字符串中查找指定分隔符（Unicode 字符）出现的位置，并返回其后面部分
     *
     * @param str         字符串
     * @param delimiter   分隔符（Unicode 字符）
     * @param isLastIndex 是否使用最后一次出现位置的索引
     * @return 子字符串，如果未找到分隔符 {@code delimiter} 则返回原字符串；如果 {@code str} 为 {@code null}，则返回 {@code null}
     */
    private static String substringAfter(CharSequence str, int delimiter, boolean isLastIndex) {
        if (isEmpty(str)) {
            return str == null ? null : str.toString();
        }

        String s = str.toString();
        int index = isLastIndex ? s.lastIndexOf(delimiter) : s.indexOf(delimiter);
        if (index == -1 || index == str.length() - 1) {
            return StringPool.EMPTY;
        }

        return substring(str, index + 1);
    }

    /**
     * 获取指定分隔符首次出现位置之后的子字符串
     *
     * <p>
     * 在字符串中查找指定分隔符（字符串）首次出现的位置，并返回其后面部分
     *
     * @param str       字符串
     * @param searchStr 分隔符（字符串）
     * @return 子字符串，如果未找到分隔符 {@code delimiter} 则返回原字符串；如果 {@code str} 为 {@code null}，则返回 {@code null}
     */
    public static String substringAfter(CharSequence str, CharSequence searchStr) {
        return substringAfter(str, searchStr, false);
    }

    /**
     * 获取指定分隔符最后一次出现位置之后的子字符串
     *
     * <p>
     * 在字符串中查找指定分隔符（字符串）最后一次出现的位置，并返回其后面部分
     *
     * @param str       字符串
     * @param delimiter 分隔符（字符串）
     * @return 子字符串，如果未找到分隔符 {@code delimiter} 则返回原字符串；如果 {@code str} 为 {@code null}，则返回 {@code null}
     */
    public static String substringAfterLast(CharSequence str, CharSequence delimiter) {
        return substringAfter(str, delimiter, true);
    }

    /**
     * 获取指定分隔符之后的子字符串
     *
     * <p>
     * 在字符串中查找指定分隔符（字符串）出现的位置，并返回其后面部分
     *
     * @param str         字符串
     * @param delimiter   分隔符（字符串）
     * @param isLastIndex 是否使用最后一次出现位置的索引
     * @return 子字符串，如果未找到分隔符 {@code delimiter} 则返回原字符串；如果 {@code str} 为 {@code null}，则返回 {@code null}
     */
    private static String substringAfter(CharSequence str, CharSequence delimiter, boolean isLastIndex) {
        if (isEmpty(str)) {
            return str == null ? null : str.toString();
        }

        if (delimiter == null) {
            return StringPool.EMPTY;
        }

        int index = isLastIndex ? lastIndexOf(str, delimiter) : indexOf(str, delimiter);
        if (index == -1 || index == str.length() - 1) {
            return StringPool.EMPTY;
        }

        return substring(str, index + delimiter.length());
    }

    /**
     * 获取两个相同标记之间的子字符串
     *
     * @param str 字符串
     * @param tag 标记（Unicode 字符）
     * @return 子字符串，如果字符串 {@code str} 为 {@code null} 或未找到标记，则返回 {@code null}
     */
    public static String substringBetween(CharSequence str, int tag) {
        return substringBetween(str, tag, tag);
    }

    /**
     * 获取两个指定分隔符之间的子字符串
     *
     * <p>
     * 在字符串中查找第一次出现的 {@code before} 和 {@code after}，并返回它们之间的子字符串
     *
     * @param str    字符串
     * @param before 起始标记（Unicode 字符）
     * @param after  结束标记（Unicode 字符）
     * @return 子字符串，如果字符串 {@code str} 为 {@code null} 或未找到标记，则返回 {@code null}
     */
    public static String substringBetween(CharSequence str, int before, int after) {
        if (str == null) {
            return null;
        }

        String s = str.toString();
        int start = s.indexOf(before);
        if (start != -1) {
            int end = s.indexOf(after, start + 1);
            if (end != -1) {
                return s.substring(start + 1, end);
            }
        }

        return null;
    }

    /**
     * 获取两个相同标记之间的子字符串
     *
     * @param str 字符串
     * @param tag 标记（字符串）
     * @return 子字符串，如果字符串 {@code str} 为 {@code null} 或未找到标记，则返回 {@code null}
     */
    public static String substringBetween(CharSequence str, CharSequence tag) {
        return substringBetween(str, tag, tag);
    }

    /**
     * 获取两个指定标记之间的子字符串
     *
     * <p>
     * 在字符串中查找标记 {@code before} 和 {@code after} 第一次出现的位置，并返回它们之间的子字符串
     *
     * @param str    字符串
     * @param before 起始标记（字符串）
     * @param after  结束标记（字符串）
     * @return 子字符串，如果字符串 {@code str} 为 {@code null} 或未找到标记，则返回 {@code null}
     */
    public static String substringBetween(CharSequence str, CharSequence before, CharSequence after) {
        if (ObjectUtil.isAnyNull(str, before, after)) {
            return null;
        }

        int start = indexOf(str, before);
        if (start != -1) {
            int end = indexOf(str, after, start + before.length());
            if (start == end) {
                return StringPool.EMPTY;
            }
            if (end != -1) {
                return substring(str, start + before.length(), end);
            }
        }

        return null;
    }

    /**
     * 获取所有两个相同标记之间的子字符串
     *
     * @param str 字符串
     * @param tag 标记（Unicode 字符）
     * @return 子字符串数组，如果字符串 {@code str} 为空或未找到标记，返回空数组
     */
    @NonNull
    public static String[] substringBetweenAll(CharSequence str, int tag) {
        return substringBetweenAll(str, tag, tag);
    }

    /**
     * 获取所有两个指定标记之间的子字符串
     *
     * @param str    字符串
     * @param before 起始标记（Unicode 字符）
     * @param after  结束标记（Unicode 字符）
     * @return 子字符串数组，如果字符串 {@code str} 为空或未找到标记，返回空数组
     */
    @NonNull
    public static String[] substringBetweenAll(CharSequence str, int before, int after) {
        if (isEmpty(str)) {
            return ArrayUtil.EMPTY_STRING_ARRAY;
        }

        String s = str.toString();
        int strLen = s.length();

        List<String> list = new ArrayList<>();
        int pos = 0;
        while (pos < strLen - 1) {
            int start = s.indexOf(before, pos);
            if (start < 0) {
                break;
            }

            start += 1;
            int end = s.indexOf(after, start);
            if (end < 0) {
                break;
            }

            list.add(substring(str, start, end));
            pos = end + 1;
        }

        if (list.isEmpty()) {
            return ArrayUtil.EMPTY_STRING_ARRAY;
        }

        return list.toArray(ArrayUtil.EMPTY_STRING_ARRAY);
    }

    /**
     * 获取所有两个相同标记之间的子字符串
     *
     * @param str 字符串
     * @param tag 标记（字符串）
     * @return 子字符串数组，如果字符串 {@code str} 为空、标记为空或未找到，返回空数组
     */
    @NonNull
    public static String[] substringBetweenAll(CharSequence str, CharSequence tag) {
        return substringBetweenAll(str, tag, tag);
    }

    /**
     * 获取所有两个指定标记之间的子字符串
     *
     * @param str    字符串
     * @param before 起始标记（字符串）
     * @param after  结束标记（字符串）
     * @return 子字符串数组，如果字符串 {@code str} 为空、标记为空或未找到，返回空数组
     */
    @NonNull
    public static String[] substringBetweenAll(CharSequence str, CharSequence before, CharSequence after) {
        if (ObjectUtil.isAnyEmpty(str, before, after)) {
            return ArrayUtil.EMPTY_STRING_ARRAY;
        }

        int strLen = str.length();
        int beforeLen = before.length();
        int afterLen = after.length();

        List<String> list = new ArrayList<>();
        int pos = 0;
        while (pos < strLen - afterLen) {
            int start = indexOf(str, before, pos);
            if (start < 0) {
                break;
            }

            start += beforeLen;
            int end = indexOf(str, after, start);
            if (end < 0) {
                break;
            }

            list.add(substring(str, start, end));
            pos = end + afterLen;
        }

        if (list.isEmpty()) {
            return ArrayUtil.EMPTY_STRING_ARRAY;
        }

        return list.toArray(ArrayUtil.EMPTY_STRING_ARRAY);
    }

    /**
     * 字符串是否以指定前缀开始
     *
     * <p>
     * 如果字符串 {@code str} 和 前缀 {@code prefix} 都为 {@code null}，则返回 {@code true}
     *
     * @param str    字符串
     * @param prefix 前缀
     * @return boolean
     */
    public static boolean startsWith(CharSequence str, CharSequence prefix) {
        return startsWith(str, prefix, false);
    }

    /**
     * 字符串是否以指定前缀开始，忽略大小写
     *
     * @param str    字符串
     * @param prefix 前缀
     * @return boolean
     */
    public static boolean startsWithIgnoreCase(CharSequence str, CharSequence prefix) {
        return startsWith(str, prefix, true);
    }

    /**
     * 字符串是否以指定前缀列表中的任意一个前缀开始
     *
     * @param str      字符串
     * @param prefixes 前缀列表
     * @return boolean
     */
    public static boolean startsWithAny(CharSequence str, CharSequence... prefixes) {
        return startsWithAny(str, NullSafeUtil.asList(prefixes));
    }

    /**
     * 字符串是否以指定前缀列表中的任意一个前缀开始
     *
     * @param str      字符串
     * @param prefixes 前缀列表
     * @return boolean
     */
    public static boolean startsWithAny(CharSequence str, Collection<? extends CharSequence> prefixes) {
        if (isEmpty(str) || CollectionUtil.isEmpty(prefixes)) {
            return false;
        }

        for (CharSequence prefix : prefixes) {
            if (startsWith(str, prefix)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 字符串是否以指定前缀列表中的任意一个前缀开始，忽略大小写
     *
     * @param str      字符串
     * @param prefixes 前缀列表
     * @return boolean
     */
    public static boolean startsWithAnyIgnoreCase(CharSequence str, CharSequence... prefixes) {
        return startsWithAnyIgnoreCase(str, NullSafeUtil.asList(prefixes));
    }

    /**
     * 字符串是否以指定前缀列表中的任意一个前缀开始，忽略大小写
     *
     * @param str      字符串
     * @param prefixes 前缀列表
     * @return boolean
     */
    public static boolean startsWithAnyIgnoreCase(CharSequence str, Collection<? extends CharSequence> prefixes) {
        if (isEmpty(str) || CollectionUtil.isEmpty(prefixes)) {
            return false;
        }

        for (CharSequence prefix : prefixes) {
            if (startsWithIgnoreCase(str, prefix)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 字符串是否以指定前缀开始
     *
     * @param str        字符串
     * @param prefix     前缀
     * @param ignoreCase 忽略大小写
     * @return boolean
     */
    private static boolean startsWith(CharSequence str, CharSequence prefix, boolean ignoreCase) {
        if (str == null || prefix == null) {
            return str == prefix;
        }

        int prefixLen = prefix.length();
        if (prefixLen > str.length()) {
            return false;
        }

        return regionMatches(str, ignoreCase, 0, prefix, 0, prefixLen);
    }

    /**
     * 字符串是否以指定后缀结束
     *
     * @param str    字符串
     * @param suffix 后缀
     * @return boolean
     */
    public static boolean endsWith(CharSequence str, CharSequence suffix) {
        return endsWith(str, suffix, false);
    }

    /**
     * 字符串是否以指定后缀结束，忽略大小写
     *
     * @param str    字符串
     * @param suffix 后缀
     * @return boolean
     */
    public static boolean endsWithIgnoreCase(CharSequence str, CharSequence suffix) {
        return endsWith(str, suffix, true);
    }

    /**
     * 字符串是否以指定后缀列表中的任意一个后缀结束
     *
     * @param str      字符串
     * @param suffixes 后缀列表
     * @return boolean
     */
    public static boolean endsWithAny(CharSequence str, CharSequence... suffixes) {
        return endsWithAny(str, NullSafeUtil.asList(suffixes));
    }

    /**
     * 字符串是否以指定后缀列表中的任意一个后缀结束
     *
     * @param str      字符串
     * @param suffixes 后缀列表
     * @return boolean
     */
    public static boolean endsWithAny(CharSequence str, Collection<? extends CharSequence> suffixes) {
        if (isEmpty(str) || CollectionUtil.isEmpty(suffixes)) {
            return false;
        }

        for (CharSequence suffix : suffixes) {
            if (endsWith(str, suffix)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 字符串是否以指定后缀列表中的任意一个后缀结束，忽略大小写
     *
     * @param str      字符串
     * @param suffixes 后缀列表
     * @return boolean
     */
    public static boolean endsWithAnyIgnoreCase(CharSequence str, CharSequence... suffixes) {
        return endsWithAnyIgnoreCase(str, NullSafeUtil.asList(suffixes));
    }

    /**
     * 字符串是否以指定后缀列表中的任意一个后缀结束，忽略大小写
     *
     * @param str      字符串
     * @param suffixes 后缀列表
     * @return boolean
     */
    public static boolean endsWithAnyIgnoreCase(CharSequence str, Collection<? extends CharSequence> suffixes) {
        if (isEmpty(str) || CollectionUtil.isEmpty(suffixes)) {
            return false;
        }

        for (CharSequence suffix : suffixes) {
            if (endsWithIgnoreCase(str, suffix)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 字符串是否以指定后缀结束
     *
     * @param str        字符串
     * @param suffix     后缀
     * @param ignoreCase 忽略大小写
     * @return boolean
     */
    private static boolean endsWith(CharSequence str, CharSequence suffix, boolean ignoreCase) {
        if (str == null || suffix == null) {
            return str == suffix;
        }

        if (suffix.length() > str.length()) {
            return false;
        }

        int strOffset = str.length() - suffix.length();

        return regionMatches(str, ignoreCase, strOffset, suffix, 0, suffix.length());
    }

    /**
     * 字符串添加前缀，若前缀已存在则忽略
     *
     * @param str    字符串
     * @param prefix 前缀
     * @return 处理后的字符串
     */
    public static String prependIfMissing(String str, String prefix) {
        return prependIfMissing(str, prefix, false);
    }

    /**
     * 字符串添加前缀，若前缀已存在(忽略大小写)则忽略
     *
     * @param str    字符串
     * @param prefix 前缀
     * @return 处理后的字符串
     */
    public static String prependIfMissingIgnoreCase(String str, String prefix) {
        return prependIfMissing(str, prefix, true);
    }

    /**
     * 字符串添加前缀，若前缀已存在则忽略
     *
     * @param str        字符串
     * @param prefix     前缀
     * @param ignoreCase 忽略大小写
     * @return 处理后的字符串
     */
    private static String prependIfMissing(String str, String prefix, boolean ignoreCase) {
        if (str == null || isEmpty(prefix) || startsWith(str, prefix, ignoreCase)) {
            return str;
        }
        return prefix + str;
    }

    /**
     * 字符串添加后缀，若后缀已存在则忽略
     *
     * @param str    字符串
     * @param suffix 后缀
     * @return 处理后的字符串
     */
    public static String appendIfMissing(String str, String suffix) {
        return appendIfMissing(str, suffix, false);
    }

    /**
     * 字符串添加后缀，若后缀已存在(忽略大小写)则忽略
     *
     * @param str    字符串
     * @param suffix 后缀
     * @return 处理后的字符串
     */
    public static String appendIfMissingIgnoreCase(String str, String suffix) {
        return appendIfMissing(str, suffix, true);
    }

    /**
     * 字符串添加后缀，若后缀已存在则忽略
     *
     * @param str        字符串
     * @param suffix     后缀
     * @param ignoreCase 忽略大小写
     * @return 处理后的字符串
     */
    private static String appendIfMissing(String str, String suffix, boolean ignoreCase) {
        if (str == null || isEmpty(suffix) || endsWith(str, suffix, ignoreCase)) {
            return str;
        }
        return str + suffix;
    }

    /**
     * 替换字符串中第一个匹配到的指定子字符串
     * <p>
     * 如果字符串 {@code str} 或 要被替换的字符串 {@code searchString}
     * 或要替换的字符串 {@code replacement} 为 {@code null} 时，返回原字符串。
     *
     * @param str          字符串
     * @param searchString 要被替换的字符串
     * @param replacement  要替换的字符串
     * @return 处理后的字符串
     */
    public static String replace(String str, String searchString, String replacement) {
        return replace(str, searchString, replacement, false);
    }

    /**
     * 替换字符串中第一个匹配到的指定子字符串，忽略大小写
     * <p>
     * 如果字符串 {@code str} 或 要被替换的字符串 {@code searchString}
     * 或要替换的字符串 {@code replacement} 为 {@code null} 时，返回原字符串。
     *
     * @param str          字符串
     * @param searchString 要被替换的字符串
     * @param replacement  要替换的字符串
     * @return 处理后的字符串
     */
    public static String replaceIgnoreCase(String str, String searchString, String replacement) {
        return replace(str, searchString, replacement, true);
    }

    /**
     * 替换字符串中第一个匹配到的指定子字符串
     * <p>
     * 如果字符串 {@code str} 或 要被替换的字符串 {@code searchString}
     * 或要替换的字符串 {@code replacement} 为 {@code null} 时，返回原字符串。
     *
     * @param str         字符串
     * @param searchStr   要被替换的字符串
     * @param replacement 要替换的字符串
     * @param ignoreCase  忽略大小写
     * @return 处理后的字符串
     */
    private static String replace(String str, String searchStr, String replacement, boolean ignoreCase) {
        if (isAnyEmpty(str, searchStr) || replacement == null) {
            return str;
        }

        int strLength = str.length();
        int searchStrLength = searchStr.length();
        if (strLength < searchStrLength) {
            return str;
        }

        // 设定默认增长尺寸，避免不必要的扩容
        int increase = 16 * Math.max(replacement.length() - searchStrLength, 0);

        int start = 0;
        int end = ignoreCase ? indexOfIgnoreCase(str, searchStr, start) : indexOf(str, searchStr, start);
        if (end == -1) {
            return str;
        }

        StringBuilder result = new StringBuilder(strLength + increase);
        while (end != -1) {
            result.append(str, start, end).append(replacement);
            start = end + searchStrLength;
            end = ignoreCase ? indexOfIgnoreCase(str, searchStr, start) : indexOf(str, searchStr, start);
        }
        result.append(str, start, strLength);

        return result.toString();
    }

    /**
     * 比较两个字符串
     *
     * @param str1 字符串1
     * @param str2 字符串2
     * @return 如果 {@code str1} 为 {@code null} 则返回 {@code -1}，
     * 如果 {@code str2} 为 {@code null} 则返回 {@code 1}，
     * 其他情况与 {@link String#compareTo(String)} 一致
     */
    public static int compare(String str1, String str2) {
        return compare(str1, str2, false);
    }

    /**
     * 比较两个字符串，忽略大小写
     *
     * <p>
     *
     * @param str1 字符串1
     * @param str2 字符串2
     * @return 如果 {@code str1} 为 {@code null} 则返回 {@code -1}，
     * 如果 {@code str2} 为 {@code null} 则返回 {@code 1}，
     * 其他情况与 {@link String#compareToIgnoreCase(String)} 一致
     */
    public static int compareIgnoreCase(String str1, String str2) {
        return compare(str1, str2, true);
    }

    /**
     * 比较两个字符串
     *
     * @param str1       字符串1
     * @param str2       字符串2
     * @param ignoreCase 忽略大小写
     * @return 如果 {@code str1} 为 {@code null} 则返回 {@code -1}，
     * 如果 {@code str2} 为 {@code null} 则返回 {@code 1}，
     * 其他情况与 {@link String#compareTo(String)} 一致
     */
    private static int compare(String str1, String str2, boolean ignoreCase) {
        if (Objects.equals(str1, str2)) {
            return 0;
        }

        if (str1 == null) {
            return -1;
        }

        if (str2 == null) {
            return 1;
        }

        return ignoreCase ? str1.compareToIgnoreCase(str2) : str1.compareTo(str2);
    }

    /**
     * 删除字符串中所有的空白符
     *
     * @param str 字符串
     * @return 处理后的字符串
     */
    public static String deleteWhitespace(String str) {
        if (isEmpty(str)) {
            return str;
        }

        int sz = str.length();
        char[] chs = new char[sz];
        int count = 0;

        for (int i = 0; i < sz; ++i) {
            if (!CharacterUtil.isWhitespace(str.charAt(i))) {
                chs[count++] = str.charAt(i);
            }
        }

        if (count == sz) {
            return str;
        }

        if (count == 0) {
            return StringPool.EMPTY;
        }

        return new String(chs, 0, count);
    }

    /**
     * 截取字符串左边指定长度
     *
     * @param str  字符串
     * @param size 截取长度
     * @return 截取的字符串，字符串 {@code str} 为 {@code null} 返回 {@code null}
     */
    public static String left(String str, int size) {
        if (isEmpty(str)) {
            return str;
        }

        if (size < 0) {
            return StringPool.EMPTY;
        }

        return str.length() <= size ? str : str.substring(0, size);
    }

    /**
     * 截取字符串右边指定长度
     *
     * @param str  字符串
     * @param size 截取长度
     * @return 截取的字符串
     */
    public static String right(String str, int size) {
        if (str == null) {
            return null;
        }

        if (size < 0) {
            return StringPool.EMPTY;
        }

        return str.length() <= size ? str : str.substring(str.length() - size);
    }

    /**
     * 字符串左边填充到指定长度
     * <p>
     * 默认填充空格，填充自定义字符请使用{@link #leftPad(String, int, char)}方法
     *
     * @param str    字符串
     * @param length 总长度
     * @return 填充后的字符串，字符串 {@code str} 为 {@code null} 返回 {@code null}
     */
    public static String leftPad(String str, int length) {
        return leftPad(str, length, CharPool.SPACE);
    }

    /**
     * 字符串左边填充字符到指定长度
     *
     * @param str     字符串
     * @param length  总长度
     * @param padChar 要填充的字符
     * @return 填充后的字符串，字符串 {@code str} 为 {@code null} 返回 {@code null}
     */
    public static String leftPad(String str, int length, char padChar) {
        if (str == null) {
            return null;
        }

        int pads = length - str.length();

        // 不需要填充
        if (pads <= 0) {
            return str;
        }
        // 填充长度超过填充限制时，使用 String 方式填充
        if (pads > PAD_LIMIT) {
            return leftPad(str, length, String.valueOf(padChar));
        }

        return repeat(padChar, pads).concat(str);
    }

    /**
     * 字符串左边填充字符串到指定长度
     *
     * <p>
     * 填充字符串为空时，默认填充空格
     *
     * @param str    字符串
     * @param length 总长度
     * @param padStr 要填充的字符串
     * @return 填充后的字符串，字符串 {@code str} 为 {@code null} 返回 {@code null}
     */
    public static String leftPad(String str, int length, String padStr) {
        if (str == null) {
            return null;
        }

        if (isEmpty(padStr)) {
            padStr = StringPool.SPACE;
        }

        int strLen = str.length();
        int padStrLen = padStr.length();
        int pads = length - strLen;

        // 不需要填充
        if (pads <= 0) {
            return str;
        }
        // 填充字符串是 1 个字符，并且填充长度小于填充限制，以 char 方法填充
        if (padStrLen == 1 && pads <= PAD_LIMIT) {
            return leftPad(str, length, padStr.charAt(0));
        }
        // 填充长度等于填充字符串长度，直接拼接
        if (pads == padStrLen) {
            return padStr.concat(str);
        }
        // 填充长度小于填充字符串长度，截取填充字符串后拼接
        if (pads < padStrLen) {
            return padStr.substring(0, pads).concat(str);
        }

        char[] padding = new char[pads];
        char[] padChars = padStr.toCharArray();
        for (int i = 0; i < pads; i++) {
            padding[i] = padChars[i % padStrLen];
        }

        return new String(padding).concat(str);
    }

    /**
     * 字符串右边填充到指定长度
     * <p>
     * 默认填充空格，填充自定义字符请使用{@link #rightPad(String, int, char)}方法
     *
     * @param str  字符串
     * @param size 总长度
     * @return 填充后的字符串，字符串 {@code str} 为 {@code null} 返回 {@code null}
     */
    public static String rightPad(String str, int size) {
        return rightPad(str, size, CharPool.SPACE);
    }

    /**
     * 字符串右边填充字符到指定长度
     *
     * @param str     字符串
     * @param size    总长度
     * @param padChar 填充的字符
     * @return 填充后的字符串，字符串 {@code str} 为 {@code null} 返回 {@code null}
     */
    public static String rightPad(String str, int size, char padChar) {
        if (str == null) {
            return null;
        }

        int pads = size - str.length();

        // 不需要填充
        if (pads <= 0) {
            return str;
        }
        // 填充长度超过填充限制时，使用 String 方式填充
        if (pads > PAD_LIMIT) {
            return rightPad(str, size, String.valueOf(padChar));
        }

        return str.concat(repeat(padChar, pads));
    }

    /**
     * 字符串右边填充字符串到指定长度
     *
     * @param str    字符串
     * @param size   总长度
     * @param padStr 填充的字符串
     * @return 填充后的字符串，字符串 {@code str} 为 {@code null} 返回 {@code null}
     */
    public static String rightPad(String str, int size, String padStr) {
        if (str == null) {
            return null;
        }

        if (isEmpty(padStr)) {
            padStr = StringPool.SPACE;
        }

        int padLen = padStr.length();
        int strLen = str.length();
        int pads = size - strLen;

        // 不需要填充
        if (pads <= 0) {
            return str;
        }
        // 填充字符串是 1 个字符，并且填充长度小于填充限制，以 char 方法填充
        if (padLen == 1 && pads <= PAD_LIMIT) {
            return rightPad(str, size, padStr.charAt(0));
        }
        // 填充长度等于填充字符串长度，直接拼接
        if (pads == padLen) {
            return str.concat(padStr);
        }
        // 填充长度小于填充字符串长度，截取填充字符串后拼接
        if (pads < padLen) {
            return str.concat(padStr.substring(0, pads));
        }

        char[] padding = new char[pads];
        char[] padChars = padStr.toCharArray();
        for (int i = 0; i < pads; i++) {
            padding[i] = padChars[i % padLen];
        }

        return str.concat(new String(padding));
    }

    /**
     * 重复指定字符指定次数生成一个新字符串
     *
     * @param c     字符
     * @param count 重复次数
     * @return 字符串，重复次数小于等于 {@code 0} 时，默认返回空字符串
     */
    @NonNull
    public static String repeat(char c, int count) {
        if (count <= 0) {
            return StringPool.EMPTY;
        }

        char[] chars = ArrayUtil.newArray(count, c);

        return new String(chars);
    }

    /**
     * 重复指定字符串指定次数生成一个新字符串
     *
     * @param str   字符串
     * @param count 重复次数
     * @return 字符串，字符串 {@code str} 为空或重复次数小于等于 {@code 0} 时，默认返回空字符串
     */
    @NonNull
    public static String repeat(String str, int count) {
        if (isEmpty(str) || count <= 0) {
            return StringPool.EMPTY;
        }

        return str.repeat(count);
    }

    /**
     * 将字符串首字母转大写
     *
     * @param str 字符串
     * @return 转换后的字符串
     */
    public static String capitalize(String str) {
        return switchFirstCharCase(str, true);
    }

    /**
     * 字符串首字母转小写
     *
     * @param str 字符串
     * @return 转换后的字符串
     */
    public static String uncapitalize(String str) {
        return switchFirstCharCase(str, false);
    }

    /**
     * 字符串首字母转小写
     *
     * @param str       字符串
     * @param upperCase 是否转大写
     * @return 转换后的字符串
     */
    private static String switchFirstCharCase(String str, boolean upperCase) {
        int strLen = length(str);
        if (strLen == 0) {
            return str;
        }

        int firstCodepoint = str.codePointAt(0);
        int newCodePoint = upperCase ? Character.toUpperCase(firstCodepoint) : Character.toLowerCase(firstCodepoint);
        if (firstCodepoint == newCodePoint) {
            return str;
        }

        int[] newCodePoints = new int[strLen];
        int outOffset = 0;
        newCodePoints[outOffset++] = newCodePoint;

        int codepoint;
        for (int inOffset = Character.charCount(firstCodepoint); inOffset < strLen; inOffset += Character.charCount(codepoint)) {
            codepoint = str.codePointAt(inOffset);
            newCodePoints[outOffset++] = codepoint;
        }

        return new String(newCodePoints, 0, outOffset);
    }

    /**
     * 将下划线命名（snake_case）转换为驼峰命名（camelCase）
     *
     * @param str 字符串
     * @return 转换后的字符串
     */
    public static String underlineToCamel(String str) {
        if (isBlank(str)) {
            return str;
        }

        StringBuilder result = new StringBuilder();
        boolean upperCaseNext = false;
        for (char c : str.toCharArray()) {
            if (c == CharPool.UNDERSCORE) {
                upperCaseNext = true;
            } else {
                result.append(upperCaseNext ? Character.toUpperCase(c) : c);
                upperCaseNext = false;
            }
        }

        return result.toString();
    }

    /**
     * 将驼峰命名（camelCase）转换为下划线命名（snake_case）
     *
     * @param str 字符串
     * @return 转换后的字符串
     */
    public static String camelToUnderline(String str) {
        if (isBlank(str)) {
            return str;
        }

        StringBuilder result = new StringBuilder();
        for (char c : str.toCharArray()) {
            if (Character.isUpperCase(c)) {
                result.append(CharPool.UNDERSCORE).append(Character.toLowerCase(c));
            } else {
                result.append(c);
            }
        }

        return result.toString();
    }

    /**
     * 将横线命名（kebab-case）转换为驼峰命名（camelCase）
     *
     * @param str 字符串
     * @return 转换后的字符串
     */
    public static String kebabToCamel(String str) {
        if (isBlank(str)) {
            return str;
        }

        StringBuilder result = new StringBuilder();
        boolean upperCaseNext = false;
        for (char c : str.toCharArray()) {
            if (c == CharPool.DASH) {
                upperCaseNext = true;
            } else {
                result.append(upperCaseNext ? Character.toUpperCase(c) : c);
                upperCaseNext = false;
            }
        }

        return result.toString();
    }

    /**
     * 将驼峰命名（camelCase）转换为横线命名（kebab-case）
     *
     * @param str 字符串
     * @return 转换后的字符串
     */
    public static String camelToKebab(String str) {
        if (isBlank(str)) {
            return str;
        }

        StringBuilder result = new StringBuilder();
        for (char c : str.toCharArray()) {
            if (Character.isUpperCase(c)) {
                result.append(CharPool.DASH).append(Character.toLowerCase(c));
            } else {
                result.append(c);
            }
        }

        return result.toString();
    }

    /**
     * 移除字符串末尾的换行符 {@code \n} {@code \r} {@code \r\n}
     *
     * @param str 字符串
     * @return 处理后的字符串
     */
    public static String chomp(String str) {
        if (isEmpty(str)) {
            return str;
        }

        if (str.length() == 1) {
            char ch = str.charAt(0);
            return ch != CharPool.CR && ch != CharPool.LF ? str : StringPool.EMPTY;
        }

        int lastIndex = str.length() - 1;
        char last = str.charAt(lastIndex);
        if (last == CharPool.LF) {
            if (str.charAt(lastIndex - 1) == CharPool.CR) {
                --lastIndex;
            }
        } else if (last != CharPool.CR) {
            ++lastIndex;
        }

        return str.substring(0, lastIndex);
    }

    /**
     * 翻转字符串
     *
     * @param str 字符串
     * @return 翻转后的字符串
     */
    public static String reverse(String str) {
        return isEmpty(str) ? str : (new StringBuilder(str)).reverse().toString();
    }

    /**
     * 删除字符串两边的空白符
     *
     * <p>
     * 空白符的定义参考{@link Character#isWhitespace(int)}
     *
     * @param str 字符串
     * @return 处理后的字符串
     */
    public static String strip(String str) {
        return isEmpty(str) ? str : str.strip();
    }

    /**
     * 删除字符串开头的空白符
     *
     * <p>
     * 空白符的定义参考{@link Character#isWhitespace(int)}
     *
     * @param str 字符串
     * @return 处理后的字符串
     */
    public static String stripLeading(String str) {
        return isEmpty(str) ? str : str.stripLeading();
    }

    /**
     * 删除字符串结尾的空白符
     * <p>
     * 空白符的定义参考{@link Character#isWhitespace(int)}
     *
     * @param str 字符串
     * @return 处理后的字符串
     */
    public static String stripTrailing(String str) {
        return isEmpty(str) ? str : str.stripTrailing();
    }

    /**
     * 删除每行的开头和结尾的空白符
     * <p>
     * 空白符的定义参考{@link Character#isWhitespace(int)}
     *
     * @param str 字符串
     * @return 处理后的字符串
     */
    public static String stripIndent(String str) {
        return isEmpty(str) ? str : str.stripIndent();
    }

    /**
     * 将字符串中的特殊字符转为 {@code html} 字符实体
     *
     * @param str 字符串
     * @return 处理后的字符串
     */
    public static String htmlEscape(String str) {
        return isEmpty(str) ? str : HtmlUtils.htmlEscape(str);
    }

    /**
     * 将字符串中的 {@code html} 字符实体转为特殊字符
     *
     * @param str 字符串
     * @return 处理后的字符串
     */
    public static String htmlUnescape(String str) {
        return isEmpty(str) ? str : HtmlUtils.htmlUnescape(str);
    }

    /**
     * 字符串转小写
     *
     * @param str 字符串
     * @return 小写字符串
     */
    public static String toLowerCase(String str) {
        return isEmpty(str) ? str : str.toLowerCase();
    }

    /**
     * 字符串转小写，指定语言
     *
     * @param str 字符串
     * @return 小写字符串
     */
    public static String toLowerCase(String str, Locale locale) {
        return isEmpty(str) ? str : str.toLowerCase(LocaleUtil.defaultIfNull(locale));
    }

    /**
     * 字符串转大写
     *
     * @param str 字符串
     * @return 大写字符串
     */
    public static String toUpperCase(String str) {
        return isEmpty(str) ? str : str.toUpperCase();
    }

    /**
     * 字符串转大写，指定语言
     *
     * @param str 字符串
     * @return 大写字符串
     */
    public static String toUpperCase(String str, Locale locale) {
        return isEmpty(str) ? str : str.toUpperCase(LocaleUtil.defaultIfNull(locale));
    }

    /**
     * 包裹字符串
     *
     * @param str    字符串
     * @param around 前缀和后缀
     * @return 包裹后的字符串，如果 {@code around} 为空，则返回原字符串
     */
    @NonNull
    public static String wrap(String str, String around) {
        return wrap(str, around, around);
    }

    /**
     * 包裹字符串
     *
     * @param str    字符串
     * @param prefix 前缀
     * @param suffix 后缀
     * @return 包裹后的字符串
     */
    @NonNull
    public static String wrap(String str, String prefix, String suffix) {
        return emptyIfNull(prefix).concat(emptyIfNull(str)).concat(emptyIfNull(suffix));
    }

    /**
     * 包裹字符串，如果已有前缀或后缀则不添加
     *
     * @param str    字符串
     * @param around 前缀和后缀
     * @return 包裹后的字符串，如果 {@code around} 为空，则返回原字符串
     */
    @NonNull
    public static String wrapIfMissing(String str, String around) {
        return wrapIfMissing(str, around, around);
    }

    /**
     * 包裹字符串，如果已有前缀或后缀则不添加
     *
     * @param str    字符串
     * @param prefix 前缀
     * @param suffix 后缀
     * @return 包裹后的字符串
     */
    @NonNull
    public static String wrapIfMissing(String str, String prefix, String suffix) {
        int length = 0;
        if (isNotEmpty(str)) {
            length += str.length();
        }

        if (isNotEmpty(prefix)) {
            length += prefix.length();
        }

        if (isNotEmpty(suffix)) {
            length += suffix.length();
        }

        StringBuilder sb = new StringBuilder(length);
        if (isNotEmpty(prefix) && !str.startsWith(prefix)) {
            sb.append(prefix);
        }

        if (isNotEmpty(str)) {
            sb.append(str);
        }

        if (isNotEmpty(suffix) && !str.endsWith(suffix)) {
            sb.append(suffix);
        }

        return sb.toString();
    }

    /**
     * 判断字符串的每个字符是否匹配
     *
     * @param str
     * @param predicate
     * @return
     */
    private static boolean allCharMatches(CharSequence str, CharPredicate predicate) {
        if (isEmpty(str)) {
            return false;
        }

        for (int i = 0; i < str.length(); i++) {
            if (!predicate.test(str.charAt(i))) {
                return false;
            }
        }

        return true;
    }

    private static boolean regionMatches(CharSequence str, boolean ignoreCase, int strStart, CharSequence subStr, int subStrStart, int length) {
        if (str instanceof String && subStr instanceof String) {
            return ((String) str).regionMatches(ignoreCase, strStart, (String) subStr, subStrStart, length);
        }

        int index1 = strStart;
        int index2 = subStrStart;
        int tmpLen = length;

        // Extract these first so we detect NPEs the same as the java.lang.String version
        int srcLen = str.length() - strStart;
        int otherLen = subStr.length() - subStrStart;

        // Check for invalid parameters
        if (strStart < 0 || subStrStart < 0 || length < 0) {
            return false;
        }

        // Check that the regions are long enough
        if (srcLen < length || otherLen < length) {
            return false;
        }

        while (tmpLen-- > 0) {
            char c1 = str.charAt(index1++);
            char c2 = subStr.charAt(index2++);

            if (c1 == c2) {
                continue;
            }

            if (!ignoreCase) {
                return false;
            }

            // The real same check as in String.regionMatches():
            char u1 = Character.toUpperCase(c1);
            char u2 = Character.toUpperCase(c2);
            if (u1 != u2 && Character.toLowerCase(u1) != Character.toLowerCase(u2)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 使用空白符分割字符串
     *
     * @param str               字符串
     * @param preserveAllTokens 是否保留所有分段
     * @return 分割后的字符串数组
     */
    private static String[] splitByWhitespace(CharSequence str, boolean preserveAllTokens) {
        int len = str.length();
        List<String> list = new ArrayList<>();
        int i = 0;
        int start = 0;
        boolean match = false;
        boolean lastMatch = false;

        while (i < len) {
            if (Character.isWhitespace(str.charAt(i))) {
                if (match || preserveAllTokens) {
                    lastMatch = true;
                    list.add(substring(str, start, i));
                    match = false;
                }
                start = ++i;
                continue;
            }
            lastMatch = false;
            match = true;
            i++;
        }

        if (match || preserveAllTokens && lastMatch) {
            list.add(substring(str, start, i));
        }

        return list.toArray(ArrayUtil.EMPTY_STRING_ARRAY);
    }

}
